package com.huawei.esdk.tp.device.smc;

import java.lang.reflect.Proxy;
import java.util.HashMap;

import org.apache.log4j.Logger;

import com.huawei.esdk.platform.authorize.itf.IAuthorize;
import com.huawei.esdk.platform.authorize.itf.IAuthorizePolicy;
import com.huawei.esdk.platform.common.MessageContext;
import com.huawei.esdk.platform.common.ThreadLocalHolder;
import com.huawei.esdk.platform.common.bean.aa.AccountInfo;
import com.huawei.esdk.platform.common.constants.ESDKConstant;
import com.huawei.esdk.platform.common.exception.SDKException;
import com.huawei.esdk.platform.common.utils.ApplicationContextUtil;
import com.huawei.esdk.platform.common.utils.StringUtils;
import com.huawei.esdk.platform.commu.itf.ICXFSOAPProtocolAdapter;
import com.huawei.esdk.tp.common.ErrInfo;
import com.huawei.esdk.tp.common.constants.InfoKeys;
import com.huawei.esdk.tp.common.constants.TPConstant;
import com.huawei.esdk.tp.device.aa.SMCAAService;
import com.huawei.esdk.tp.device.base.TPDeviceConnectionBase;
import com.huawei.esdk.tp.southcommu.cxf.gen.AuthenticateResponseSouth;
import com.huawei.esdk.tp.southcommu.cxf.gen.KeepAliveResponseSouth;
import com.huawei.esdk.tp.southcommu.cxf.gen.LoginRequestResponseSouth;
import com.huawei.esdk.tp.southcommu.cxf.gen.LogoutResponseSouth;

public class SMCConnection extends TPDeviceConnectionBase
{
    private static final Logger LOGGER = Logger.getLogger(SMCConnection.class);
    
    private SMCDevice device;
    
    private SMCAAService smcLoginProcess;
    
    private IAuthorizePolicy authorizePolicy;
    
    private IAuthorize authorize = ApplicationContextUtil.getBean("authorize");
    
    HashMap<Class<?>, Object> mapFunc = new HashMap<Class<?>, Object>();
    
    public SMCConnection(ICXFSOAPProtocolAdapter cxfSOAPProtocolAdapter, SMCDevice serviceProxy, String user,
        String pwd, IAuthorizePolicy authorizePolicy)
    {
        super(user, pwd);
        LOGGER.debug("user = " + user);
        this.device = serviceProxy;
        smcLoginProcess = new SMCAAService(cxfSOAPProtocolAdapter);
        this.authorizePolicy = authorizePolicy;
    }
    
    @Override
    public Object getServiceProxy(Class<?>[] itfs)
        throws SDKException
    {
        prepareForSessionId((String)getAdditionalData("connId"));
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), itfs, device.getService(itfs));
    }
    
    @Override
    public boolean doHeartbeat(String connId)
    {
        try
        {
            prepareForSessionId(connId);
        }
        catch (SDKException e)
        {
            return false;
        }
        
        KeepAliveResponseSouth res = (KeepAliveResponseSouth)smcLoginProcess.smcKeepAlive();
        if (0 == res.getKeepAliveResult().getErrCode())
        {
            return true;
        }
        
        return false;
    }
    
    @Override
    public boolean initConn(String connId)
    {
        MessageContext mc = ThreadLocalHolder.get();
        AccountInfo devAcctInfo = getDevAcctInfo(authorizePolicy);
        if (null == devAcctInfo)
        {
            LOGGER.error("Cannot derive account from the input user " + getLoginUser());
            return false;
        }
        String userName = devAcctInfo.getUserId();
        String pwd = devAcctInfo.getPassword();
        
        prepareSessionId(connId);
        AuthenticateResponseSouth authRes;
        if (isLocalAuth())
        {
            LoginRequestResponseSouth loginRes = smcLoginProcess.smcLoginRequest(userName);
            if (null == loginRes)
            {
                return false;
            }
            //DTS2015011901913 修改
            else if (0 != loginRes.getLoginRequestResult().getErrCode())
            {
                mc.getEntities().put(TPConstant.SMC_LOGIN_STATUS, 
                    loginRes.getLoginRequestResult().getErrCode());
                return false;
            }
            // after loginrequest,save smc session edit by taochunlong
            String session = (String)mc.getEntities().get(InfoKeys.SMC_SESSION_ID.toString());
            saveSMCSession(session);
            authRes = smcLoginProcess.smcAuthorize(loginRes.getRandomSequence(), pwd);
        }
        else
        {
            String session = (String)mc.getEntities().get(InfoKeys.SMC_SESSION_ID.toString());
            
            //Pass through
            saveSMCSession(session);
            authRes = smcLoginProcess.smcAuthorize(pwd);            
        }
        mc.getEntities().put(TPConstant.SMC_LOGIN_STATUS, authRes.getAuthenticateResult().getErrCode());
        
        if ( 0 == authRes.getAuthenticateResult().getErrCode())
        {
            return true;
        }
        
        return false;
    }
    
    private void saveSMCSession(String smcSession)
    {
        //To add the check null condition for resolving Reconnecting SMC issue (SMC restart)
        if (null != smcSession)
        {
            setSession(smcSession);
        }
        else
        {
            LOGGER.info("smc session id = null");
        }
    }
    
    protected boolean isLocalAuth()
    {
        //After the first connection, then the production is stored into the connection itself;
        //it's for reconnect case especially
        String product = (String) getAdditionalData(ESDKConstant.CURRENT_PRODUCT);
        if (StringUtils.isEmpty(product))
        {
            //For TPOA, the product will be stored into the thread variable
            product = (String) ThreadLocalHolder.get().getEntities().get(ESDKConstant.CURRENT_PRODUCT);
        }
        //After above two tries, it's default to TP
        if (StringUtils.isEmpty(product))
        {
            product = "tp";
        }
        
        setAdditionalData(ESDKConstant.CURRENT_PRODUCT, product);
        if (authorize.isLocalAuth(product))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    @Override
    public void destroyConn(String connId)
    {
        try
        {
            prepareForSessionId(connId);
        }
        catch (SDKException e)
        {
            return;
        }
        
        MessageContext mc = ThreadLocalHolder.get();
        if (mc == null)
        {
            mc = new MessageContext();
            ThreadLocalHolder.set(mc);
        }
        mc.getEntities().put(InfoKeys.SDK_SESSION_ID.toString(), connId);
        
        LogoutResponseSouth logoutResponseSouth = smcLoginProcess.smcLogout();
        if (null != logoutResponseSouth.getLogoutResult())
        {
            mc.getEntities().put(TPConstant.SMC_LOGOUT_STATUS, (long)logoutResponseSouth.getLogoutResult().getErrCode());
        }
    }
    
    @Override
    public int getKeepAliveTimes()
    {
        return 1;
    }
    
    @Override
    public int getKeepAlivePeriod()
    {
        return 10;
    }
    
    public void prepareForSessionId(String connId)
        throws SDKException
    {
        super.prepareSessionId(connId);
        MessageContext mc = ThreadLocalHolder.get();
        if (null != getSession())
        {
            mc.getEntities().put(InfoKeys.SMC_SESSION_ID.toString(), getSession());
        }
        else
        {
            SDKException ex = new SDKException("SMC session is empty");
            ex.setSdkErrCode(ErrInfo.AUTHORIZE_ERRORCODE);
            throw ex;
        }
    }

    public SMCDevice getDevice()
    {
        return device;
    }

    public void setDevice(SMCDevice device)
    {
        this.device = device;
    }

    public SMCAAService getSmcLoginProcess()
    {
        return smcLoginProcess;
    }

    public void setSmcLoginProcess(SMCAAService smcLoginProcess)
    {
        this.smcLoginProcess = smcLoginProcess;
    }

    public IAuthorizePolicy getAuthorizePolicy()
    {
        return authorizePolicy;
    }

    public void setAuthorizePolicy(IAuthorizePolicy authorizePolicy)
    {
        this.authorizePolicy = authorizePolicy;
    }

    public IAuthorize getAuthorize()
    {
        return authorize;
    }

    public void setAuthorize(IAuthorize authorize)
    {
        this.authorize = authorize;
    }
}
